﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.DynamicData.ModelProviders;
using System.Collections.ObjectModel;
using NHibernate.Mapping;
using Mpu.NHibernateDataModelProvider.ColumnProviders;
using DynamicData.Entities;
using NHibernate.Linq;

namespace Mpu.NHibernateDataModelProvider
{
    public class NHibernateTableProvider : TableProvider
    {
        private List<NHibernateColumnProviderBase> columns = new List<NHibernateColumnProviderBase>();

        public NHibernateTableProvider(DataModelProvider model, PersistentClass persistentClass)
            : base(model)
        {
            this.Name = persistentClass.Table.Name;
            this.EntityType = persistentClass.MappedClass;

            if (persistentClass.Table.PrimaryKey != null)
            {
                foreach (var pkColumn in persistentClass.Table.PrimaryKey.ColumnIterator)
                {
                    this.columns.Add(new NHibernatePrimaryKeyColumnProvider(this, pkColumn));
                }
            }

            foreach (var property in persistentClass.PropertyIterator)
            {
                var columnProvider = GetColumnProvider(property);
                if (columnProvider != null)
                    this.columns.Add(columnProvider);
            }
        }

        public void Initialize()
        {
            foreach (var column in this.columns)
            {
                column.Initialize();
            }

            foreach (var column in this.columns)
            {
                column.InitializeAssociation();
            }
        }

        private NHibernateColumnProviderBase GetColumnProvider(Property property)
        {
            if (property.Value is ManyToOne)
            {
                var column = property.ColumnIterator.OfType<Column>().FirstOrDefault();
                return new NHibernateManyToOneColumnProvider(this, property, column);
            }
            else
            {
                if (property.Value is SimpleValue)
                {
                    var column = property.ColumnIterator.OfType<Column>().FirstOrDefault();
                    return new NHibernateSimpleValueColumnProvider(this, column);
                }
                else
                {
                    if (property.Value is Collection)
                    {
                        //Use Element property

                        if (property.Value is Bag)
                        {
                            var element = ((Bag)property.Value).Element;
                            if (element is OneToMany)
                            {
                                return new NHibernateOneToManyColumnProvider(this, property, (OneToMany)element);
                            }
                        }
                    }
                }
            }

            return null;
            throw new NotSupportedException("PASHA!!!");
        }

        private bool IsPrimaryKeyColumn(Column column, Table table)
        {
            return table.HasPrimaryKey && table.PrimaryKey.Columns.Any(c => c.Name == column.Name);
        }

        public override ReadOnlyCollection<ColumnProvider> Columns
        {
            get { return columns.Cast<ColumnProvider>().ToList().AsReadOnly(); }
        }

        public override IQueryable GetQuery(object context)
        {
            var nhibernateContext = (GenericNHibernateContext)context;
            var method = GetMethod<NHibernate.ISession>(s => s.Linq<object>());
            var genericMethod = method.MakeGenericMethod(this.EntityType);
            return (IQueryable)genericMethod.Invoke(nhibernateContext.Session, new object[1] { nhibernateContext.Session });

            //return nhibernateContext.Session.Linq<Book>();
        }

        private System.Reflection.MethodInfo GetMethod<T>(System.Linq.Expressions.Expression<Action<T>> expr)
        {
            return ((System.Linq.Expressions.MethodCallExpression)expr.Body)
                .Method
                .GetGenericMethodDefinition();
        }
    }
}
